#ifndef CONVOLUTIONNET_H
#define CONVOLUTIONNET_H

#include <vector>
#include <qmath.h>
#include <QDebug>
#include "histogram_dialog.h"
#include <QObject>
#include <QThreadPool>
#define AT_BIT(k, n) ((k >> n) & 1)

union able
{unsigned char a[8];
double b;
};

using namespace std;



class nucleus //Класс ядра
{
private:
    vector<double> w;   /*Вектор весов*/
    int size;           /*Размер ядра*/
    int hight;          /*Высота ядра*/

public:
    nucleus();

    nucleus(int ht);

    nucleus(int ht, double* ww);    /*Конструктор с известными весами*/

    ~nucleus();

    void set_nucleaus(double* wz);  /*Процедура устанавливает веса*/

    vector<double> get_nucleaus();  /*Функция возвращает веса*/

    void set_hight(int ht);         /*Устанавливает высоту ядра*/

    int get_hight();                /*Получает высоту ядра*/

    int get_size();                 /* Получает размер ядра*/

    double barrier_function(double _out);       /*Барьерная функция*/

    double calculation(vector<double> pImage);  /*Вычисление для фрагмента image, какого либо изображения*/

    double compresion(vector<double> pImage);   /*Вычисление для компрессии*/

    void random_correct(int e);
};

class matrix /*Класс матрица в  него можно переводить изображение, потом создадим специальную процедуру*/
{
private:
    int size;   /*Размер изображения*/
    int hight;  /*Высота*/
    int color_depth;    /*Глубина цвета*/
    vector<double> p;   /*Вектор точек*/

    friend class layer_two;

public:
    matrix(int ht, int dt); /*Конструктор пустого*/

    matrix(int ht, int dt, double* pt); /*Конструктор со знанием точек*/

    ~matrix();

    void set_depth(int dt); /*Устанавливаем глубину */

    void set_hight(int ht); /*Устанавливаем высоту*/

    /*Возвращаем значения*/
    int get_depth();
    int get_hight();
    int get_size();

    void set_matrix(double* pz); // устанавливает веса

    vector<double> get_matrix(); // получает веса

    matrix get_fragment(int ht, int _x, int _y); //Возвращает матрицу фрагмента изображения шириной wt и высотой ht начиная с точки с номером х (левый верхний угол)

};


class single_sigmoid
{
private:
    //const double a=1;
    double w0;
    vector<double> w;
    int count; //количество входов
    int count_image; //количество образов которые может распознать сеть (в нашем случае количество цифр)

public:
    single_sigmoid(int kol,int kol_image);

    double barrier_function(double _out);

    int calculation(vector <double> scalar); //возвращает номер образа (в нашем случае цифру)

    int once_correct(pair<vector <double>, int>  training_set, int era,int nomer);


}; // end single_sigmoid

//   ВНИМАНИЕ ИЗМЕНЕНИЕ!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//Добавляю класс сети персептонов
class net_sigmoid
{
private:
    vector<single_sigmoid> w; //Вектор персептонов
    int count; //их количество, у нас будет 10 тк 10 образцов
public:
     net_sigmoid(int count, int kol,int kol_image); //Конструктор count - количество персептонов, kol - размер входного вектора получаемого сверточной (те у нас будет 24)
     int learning_one_era (vector <pair<vector <double>, int> >  training_set, int era); //Эра обучения по всем образцам и персептронам
     void learning (vector <pair<vector <double>, int> >  training_set); //Общее обучение
     vector <int> calculation_list (vector <double>  training_set); //Выдает результат работы (те размер вектора будет 10 если к примеруон будет (1 0 ... 0) это значит что первый персептрон говорит да, а остальные нет )

};

class layer_two /*Класс реализует сразу 2 слоя сети Свертывание и сжатие*/
{
private:
    int count_of_nucleus; /*Количество ядер*/
    vector <nucleus> nuc; /*Ядра*/
    nucleus compression_ratio; /*Ядро коэффициента сжатия*/

public:
    layer_two();

    layer_two(int count, vector <nucleus> n, nucleus comp);

    matrix calculation_once(matrix _matrix, nucleus _nucleus); //Вычисление выходной матрицы, после работы сети с матрицей _matrix и ядром _nucleus

    vector <matrix> calculation (vector <matrix> mas_image); //свертывание с mas_image - вектор входных матриц

    matrix compression_once(matrix _matrix); //Вычисление выходной матрицы, после сжатия матрицы _matrix

    vector <matrix>  compression (vector <matrix> mas_image); //сжатие mas_image - вектор входных матриц

    void random_correct(int e);
};

class MyRunnable; // forward declaration

class convolution_net : public QObject
{
    Q_OBJECT
    friend class MyRunnable;

signals:
    void needToUpdateForm();

private:
    layer_two* layer_12; //Первые 2 слоя сети
    layer_two* layer_34; /*3 и 4 слои*/
    layer_two* layer_56;/* 5 слой*/
 //   ВНИМАНИЕ ИЗМЕНЕНИЕ!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    net_sigmoid* layer_7;

    int e; // число эпох

public:
    convolution_net();

    vector <int> calculation(vector <matrix> _matrix);

    vector<double> calculation_without_sigmoid(vector <matrix> _matrix);

    int sum_error(vector <pair<matrix, int> > training_set );
    double sum_error_new(vector <pair<matrix, int> > training_set );

    void random_correct(int e);

    void run_era_with_histograms(vector <pair<matrix, int> > set);

    void learning(vector <pair<matrix, int> > training_set);
    void learning_new(vector <pair<matrix, int> > training_set);
    //   ВНИМАНИЕ ИЗМЕНЕНИЕ!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Формирует обучающую выборку для последнего слоя
    vector <pair<vector <double>, int> > in_double(vector <pair<matrix, int> > training_set);
};

class MyRunnable : public QRunnable
{
    pair<matrix, int>* myWork;
    int* result;
    convolution_net* owner;

public:
    MyRunnable() : myWork(0), result(0), owner(0) {}

    void setWork(pair<matrix, int>* ptr, int* p_to_save_result, convolution_net* p_owner);

    void run ();
};

#endif // CONVOLUTIONNET_H
